home *** CD-ROM | disk | FTP | other *** search
/ Kit PC World De Ampliacion De Windows 95 / Kit PC World de ampliacion de Windows 95.iso / internet / sweeper / samples / olecon~1 / readme.txt < prev   
Text File  |  1995-12-05  |  52KB  |  1,157 lines

  1.  
  2. The OLE Controls Framework Sample Code for Authoring non-MFC Controls
  3. =====================================================================
  4.  
  5.  
  6.     This document is a short discussion on using the OLE Controls Framework to author
  7. new OLE controls.
  8.  
  9.  
  10. 0. Contents
  11. =---------=
  12.  
  13. 1.0  Introduction
  14.    1.1  Target Audience
  15.    1.2  Structure of the Framework
  16.    1.3  Target Environment
  17.  
  18. 2.0  Creating an OLE Control
  19.    2.1  Using the Control Wizard
  20.    2.2  Building the Control
  21.    2.3  Working with the In-Process server
  22.  
  23. 3.0  Working with your OLE Control
  24.    3.1  Structure of a Control
  25.    3.2  Painting a Control
  26.    3.3  Handling Messages in a Control
  27.    3.4  Adding a Propert
  28.    3.5  Adding a Method
  29.    3.6  Adding an Event
  30.    3.7  Using Standard OLE Types
  31.    3.8  Throwing an Exception
  32.  
  33. 4.0  Persistence
  34.    4.1  Text Persistence
  35.    4.2  Binary Persistence
  36.  
  37. 5.0  Property Pages
  38.    5.1 Working with a Property Page
  39.    5.2 Navigating through Associated Objects
  40.    5.3 Marking your Page as Dirty.
  41.   
  42. 6.0  String Manipulation
  43.    6.1 Types of Strings
  44.    6.2 Working with Strings
  45.  
  46. 7.0  Localization
  47.    7.1  Setting up for Localization
  48.  
  49. 8.0 Creating an Internet Aware Control
  50.  
  51. 9.0 Miscellaneous
  52.    9.1 Recommended Reading
  53.    9.2 Host Specific Notes
  54.       9.2.1 Microsoft Access 95
  55.  
  56.  
  57. 1.0 Introduction
  58. =--------------=
  59.  
  60.     The OLE Controls Framework is a sample code base from which one can author new OLE
  61. controls for use in existing containers, such as Microsoft Visual Basic, Microsoft Access,
  62. and Microsoft FoxPro, or in future containers of OLE Controls.
  63.  
  64.     It differs from the Microsoft Visual C++ CDK in many ways.  Most notably, this
  65. framework is intended to be considerably more 'bare bones'.  Only minimal functionality
  66. is provided to the programmer.  Little in the way of default handlers for various windows
  67. messages, OLE events et al has been provided.  The ability to add them all is there, but
  68. the code is not.  The code base has also been architected primarily for performance and
  69. reduced code size as much as possible.  Whenever a choice between ease of use and
  70. performance arose, the latter was typically chosen.
  71.  
  72.     The code base is, however, extremely extensibile, and, of course, all the source
  73. code is there -- if something doesn't do what you want it to, make it.
  74.  
  75.  
  76.  
  77.   1.1 Target Audience
  78.  
  79.     This framework targets a slightly more advanced programmer than the Microsoft
  80. Visual C++ Control Develoers Kit.  Specifically, the programmer will be required to under-
  81. stand some of the fundamentals of OLE automation and dual interfaces.  The user will have
  82. to be able to understand and modify an .ODL file on their own.  In addition, the user will
  83. be required to understand and be able to work with OLE persistence interfaces, most
  84. notably IStream and IPersistStream.  However, if it is not desired, the user will not
  85. be requried to have much knowledge of OLE embedding interfaces.
  86.  
  87.     Programmers who do not have specific performance requirements, those not familiar
  88. with many of the pertinent OLE technologies, or those who work primarily with the 
  89. Microsoft Foundation Classes will find the MFC/CDK far more suited to their needs.
  90.  
  91.  
  92.  
  93.  
  94.    1.2 Structure of the Framework
  95.  
  96.     The directory structure of the OLE Controls Framework is as follows:
  97.  
  98.  
  99.     \Controls +
  100.           |- \AutoSample +
  101.           |             |- \Debug
  102.           |             |- \Release
  103.           |- \Button    +
  104.           |             |- \Debug
  105.           |             |- \Release
  106.           |- \Circle    +
  107.           |             |- \Debug
  108.           |             |- \Release
  109.           |- \FontColor +
  110.           |             |- \Debug
  111.           |             |- \Release
  112.           |- \Framework +
  113.           |             |- \Debug
  114.           |             |- \Release
  115.           |- \Include
  116.           |- \Invisible +
  117.           |             |- \Debug
  118.           |             |- \Release
  119.           |- \Localize  +
  120.           |             |- \Debug
  121.           |             |- \Release
  122.           |             |- \French
  123.           |- \Template
  124.           |- \Wizards
  125.  
  126.  
  127.     The \Framework and \Include directories contain the core code for writing an OLE
  128. control.  The Include directory contains the headers that most controls will get their
  129. information from, and the Framework directory contains the core functionality [in the
  130. COleControl class] which compiles in to a library [.LIB] form.
  131.  
  132.     In addition, the Wizards and Template directories contain the necessary files to
  133. generate a control.  In the Wizards directory is a Microsoft Visual Basic 4.0 Project,
  134. CtlWiz.Vbp.  Running this project under VB4 will generate a skeleton control for you
  135. which will compile right away.  [Note: this wizard pretty much assumes you're going to
  136. place the control under the \Controls directory -- most of the paths generated are 
  137. relative and will look for the \Framework directory in ..\Framework]
  138.  
  139.     A few samples are provided with the Framework.  The Circle sample demonstrates the
  140. most trivial of OLE controls -- it has no interesting properties, no property pages, and
  141. only draws a green circle.  The Button sample demonstrates writing a simple subclassed
  142. Windows and how to change various properties on it, and fire events in response to 
  143. windows events.  The Invisible sample shows how to write an Invisible at Runtime control.
  144. The Localize sample demonstrates how to support satellite DLL localization in your server.
  145. The AutoSample is a sample of how to write an OLE Automation server, which this framework
  146. supports.  To create an automation server, Wizards\AutoWiz.Vbp should be run.
  147. Finally, the FontColor sample demonstrates how to get around an OLE automation problem
  148. with properties of types from imported type libraries [see Section 3, "Working with your
  149. OLE Control" for more information].
  150.  
  151.  
  152.  
  153.  
  154.    1.3  Target Environment
  155.  
  156.     This framework was developed assuming you have the Microsoft Visual C++ 4.0
  157. toolset in your path.  The makectl.inc and tools.inc in the \Include directory use various
  158. tools from the toolset.  In addition, the various wizards assume you have uuidgen.exe,
  159. which is included in the Microsoft Win32 SDK as well as VC4.  If you are using a different
  160. environment, it should not be terribly difficult to change the variables to work with it
  161. instead.  If you only have the Visual C++ 2.x tools and headers in your path, you will
  162. get various compile errors along the way.
  163.  
  164.     All of the makefiles and build processes are command-line based.  Various people
  165. have reported, however, that it's largely trivial to integrate that into their favorite
  166. environments.
  167.  
  168.     NOTE: Under Windows 95, Visual C++ will not, by default, register it's environment
  169. variables to set up for command line builds.  To enable command line building under
  170. Windows 95, you'll either have to chdir c:\msdev\bin and type 'vcvars32 x86' to set up
  171. your environemnt variables, or include the above file in your autoexec.bat file. 
  172. Occasionally, you will get a bunch of "Out of Environment Space" messages when doing
  173. this.  In the properties dialog for the Command Prompt, you can increase the size of the
  174. environment from 'Auto' to some number like 1024, and this takes care of the problem.
  175. None of this should happen when developing under Windows NT Workstation.
  176.  
  177.     The file 'dwinvers.h' is a file that contains version and copyright information
  178. that should be generated each time you run your builds.  The framework does not do 
  179. this work for you.
  180.  
  181.  
  182.  
  183.  
  184. 2.0 Creating an OLE Control
  185. =-------------------------=
  186.  
  187.     Creating an OLE control using this framework isn't a terribly difficult task.  The
  188. provided wizards will do most of the work for you, but we will go into the process in 
  189. brief here.  Templating off the sample code should be able to fill in the holes.
  190.  
  191.     The framework implements the core functionality in a few C++ classes, notably
  192. CAutomationObject, COleControl [which inherits from CAutomationObject], and
  193. CPropertyPage.  All objects inherit from CUnknownObject, which provides the support for
  194. aggregation.
  195.  
  196.     So, to write an OLE Control, you need to declare a new object which inherits from
  197. COleControl.  In addition, you'll need to inherit from some sort of Automation interface
  198. that describes the properties and methods for your control, say, IMyControl.
  199. This interface description is generated by MKTYPLIB and will be put in some output file
  200. created by MKTYPLIB [the Wizard sets up an environment where this file is named
  201. MyControlInterfaces.H].  COleControl has a number of virtual methods that are declared as
  202. pure, which you simply must implement in your control class.  These include, WindowProc,
  203. LoadBinaryState, LoadTextState, SaveBinaryState, SaveTextState, OnDraw, and
  204. RegisterClassData.
  205.  
  206.     To write a property page, you declare a new object which inherits from
  207. CPropertyPage.  This object must implement a DialogProc.  You can also implement
  208. automation objects and collections by declaring a new object that inherits from
  209. CAutomationObject.
  210.  
  211.     Since an OLE control is an In-Process OLE Server, you also need one file to
  212. describe all your objects, whether they be controls, automation objects, or property 
  213. pages.  This file will have a bunch of information in it, including a table of all
  214. objects and information about them.  In addition, it'll have information on whatsort of 
  215. localization your server would like to use, and what sort of licensing support you'd like
  216. to have.
  217.  
  218.     Finally, you'll need a resource file, an .ODL file to describe your interfaces and
  219. event interfaces, a .DEF file for your linking information, and a file to define all the
  220. guids that have been declared.
  221.  
  222.     While you can template all this information from one of the sample controls, using
  223. the wizard to create a new control remains the easiest way.
  224.  
  225.  
  226.  
  227.  
  228.    2.1 Using the Control Wizard
  229.  
  230.     The control wizard is a simple Visual Basic 4.0 program that can be used to create
  231. a new OLE Control project.  It is not the most horribly robust program, and usually will
  232. just abort if something odd comes up, but it will save you a considerable amount of time
  233. in the beginning.
  234.  
  235.     We will now walk through a sample to create a control.  We will assume that the
  236. Framework lies in C:\Controls, and we will create a subclassed scrollbar control.
  237.  
  238.     First, start up VB4, and run CtlWiz.Vbp.  It will ask you for the name of your
  239. new control.  We will use SuperScroll.  We will choose to subclass a windows control,
  240. and will use satellite localization, since foreign markets are important to us, and 
  241. we will not choose to avoid long file names.
  242.  
  243.     On the next screen, from the combo box, we will choose the SCROLLBAR window
  244. class.  After that, we will choose to put the project in C:\Controls\SuperScroll. [please
  245. note that the end project generated by this wizard will only work if you choose to put
  246. it in a sub-directory of your framework root, ie C:\Controls].  Finally, we will be
  247. asked for the Location of the Template files.  We will enter C:\Controls\Template.
  248.  
  249.     The control wizard will then generate your project, assuming that uuidgen.exe
  250. is somewhere in your PATH.  This will not be the speediest of processes, since I am not
  251. a Basic Lord[tm] [on a 486/66, this takes about 30 seconds].
  252.  
  253.  
  254.  
  255.  
  256.    2.2  Building the Control
  257.  
  258.     To build your control, you first need to generate the libraries for the Framework
  259. files.  Doing this is as simple as going to the \Framewrk directory, and then in each of
  260. the Debug and Release Directories, typing 'make dep all'.
  261.  
  262.     Once the framework files have been built, go back to your control's Debug and/or
  263. Release directories, and type 'make dep all' there.  This will build your control.  you
  264. will only ever need to recompile the framework files if you make a change to a file in the
  265. \Include or \Framewrk directories.
  266.  
  267.  
  268.  
  269.  
  270.    2.3  Working with the In-Process server
  271.  
  272.     Your OLE control, any property pages, and automation objects are all just OLE COM
  273. objects in an in-process server.  All of these objects must be declared in a global table,
  274. g_ObjectInfo, which is found in the main in-proc server file.  Each object is declared
  275. with a wrapper; one of CONTROLOBJECT, PROPERTYPAGE, or AUTOMATIONOBJECT.  The name of
  276. the object is entered as an argument to the macro.  In the header file where you declare
  277. the COM object, you'll need to use one of DEFINE_PROPERTYPAGEOBJECT, DEFINE_CONTROLOBJECT,
  278. or DEFINE_AUTOMATIONOBJECT to actually declare the object for use in the global table.
  279. If it turns out that you are declaring objects that aren't creatable from a class factory,
  280. they still do need to be declared in the global table, but Creation function specified
  281. in the structure should be left as NULL.
  282.  
  283.     There is some additional information that must be put in the file for the in-
  284. process server.  The LIBID of the type library [.TLB] must be put in the global variable
  285. g_pLibid.  You must indicate what sort of localization your control supports by using the
  286. variables g_fSatelliteLocalization and g_lcidLocale.   See Section 7.0 "Localization" for
  287. more information about this.
  288.  
  289.     In your in-proc server file, there are five routines that you can put code in. The
  290. first two are called when the DLL is first loaded and unloaded in to memory.  this is a
  291. good place to do any sort of initialization that can't be put off until later.  It is
  292. worth noting that for performance reasons, delaying as much as possible is often a good
  293. idea.  The CheckForLicense function lets you decide if the control is licensed or not to
  294. run.  The global variables g_wszLicenseKey and g_wszLicenseLocation describe the license
  295. key and the location in the registry of the license location.  if you don't bother with
  296. licensing, then you can leave all of the above untouched.
  297.  
  298.     The RegisterData and UnregisterData routines are called from DllRegisterServer and
  299. DllUnregisterServer, and can be used to register and clean up additional information in
  300. the registry.
  301.  
  302.     Finally, two small pieces of code are included in this file so that your project
  303. does not have to link with any of the C-runtimes.  This typically results in smaller DLL
  304. size, and can help with performance.  If you want to link with the C runtime libraries,
  305. or the CRTDLL libraries, then you can remove these last things from the in-proc server
  306. file.
  307.  
  308.  
  309. 3.0  Working with your OLE Control
  310. =--------------------------------=
  311.  
  312.     Once you have your control up and running, you'll want to start extending it's
  313. functionality.  The first thing to note is that your control is, in many ways, much like
  314. a regular Windows window.  You have an HWND, you have a window proc, and you have to paint
  315. the client area yourself using regular windows drawing APIs and handles to Device Contexts 
  316. [DC's].
  317.  
  318.  
  319.  
  320.  
  321.    3.1 Structure of a Control
  322.  
  323.     There is a core set of methods that every control in this framework must
  324. implement, based on creation semantics and methods that COleControl simply does not
  325. provide for you.  There are also a bunch of routines that are interesting to override
  326. and provide an implementation for.  The following is a discussion of many of these.
  327.  
  328.     a. static Create() function.  Using the control wizard, this routine is generated
  329. for you.  Every control must create their control object in this routine, and then return
  330. a pointer to it's private unknown [for aggregation support].
  331.  
  332.     b. Constructor and Destructor.  These are also generated for you, and a control
  333. should initialize anything here.  Controls should try to minimize the amount of work that
  334. is done here in order to help prevent load time from degrading unacceptably.
  335.  
  336.     c. RegisterClassData().  All controls must implement this routine.  This routine
  337. will only be called once the first time a control of a given type is loaded in a process.
  338. Controls should register their window class [using RegisterClass and the WNDCLASS 
  339. structure] here.  In addition, subclassed windows controls should get a pointer to the
  340. parent control's WindowProc and set that up in the g_ObjectInfo table using the
  341. SUBCLASSWNDPROCOFCONTROL() macro.  See the Button and Circle control samples for examples
  342. of how this is done.    Invisible at Runtime controls should just return FALSE in this
  343. routine, as it should never get called.
  344.  
  345.     d. BeforeCreateWindow() and AfterCreateWindow() are not mandatory to implement,
  346. but are extremely interesting routines.  BeforeCreateWindow is called right before the
  347. call to CreateWindow(), but after persistent state has been loaded.  Controls should use
  348. this opportunity to set the window title for their control in m_szWindowTitle, and can
  349. also set up bits in m_dwWindowStyle, and m_dwWindowStyleEx for calls to CreateWindowEx.
  350. Doing this work here instead of the WM_CREATE case typically results in better per-
  351. formance.
  352.  
  353.     e. InternalQueryInterface().  Your control implements this to support the QI for
  354. your primary dispatch interface, such as IMyControl.  You can also use this method to
  355. support additional interfaces in your control.  For example, if you want to support
  356. IPerPropertyBrowsing, you'd have your CMyControl class inherit from IPerPropertyBrowsing,
  357. and support the QueryInterface for IID_IPerPropertyBrowsing in InternalQueryInterface.
  358. If you fail the QI, then you should delegate back to COleControl::InternalQueryInterface
  359. to see if it likes the IID.
  360.  
  361.     f. LoadTextState, LoadBinaryState, SaveTextState, SaveBinaryState.  All controls
  362. must implement these persistence interfaces.  See Section 4.0 "Persistence" for a 
  363. discussion of these interfaces.
  364.  
  365.     g. OnDraw.  This routine is called when your control is expected to draw itself.
  366. In Design mode, this call will originate from a container calling IViewObject2::Draw.  In
  367. run mode, the controls framework will intercept the WM_PAINT message, and will translate
  368. it into a call to your OnDraw routine.  See section 3.2 "Painting a Control" for more
  369. information on this routine.
  370.  
  371.     h. WindowProc.  Messages that are not handled in the framework code [such as
  372. SimpleFrame messages and WM_PAINT], are sent to your control here.  Your control should
  373. deal with these here.  See Section 3.3 "Handling Messages in a Control" for a discussion
  374. of this routine.  In addition, please see the note below on OnSpecialKey for more
  375. information.
  376.  
  377.     i. OnSpeicalKey.  Messages for various keyboard events, such as moving the Cursor
  378. keys, function keys, and other non-standard keys do not go to the WindowProc.  Instead,
  379. they are sent to the OnSpecialKey routine.  Controls that want to handle special keys
  380. and accelerators should override and implement this routine.  They should return TRUE if
  381. they handle a key, or FALSE if they ignored it.
  382.  
  383.     j. DoCustomVerb.  If your control chooses to implement custom verbs in addition
  384. to the property page one that is provided by default [provided your control has a property
  385. page], then you should implement this routine, and take appropriate action depending on
  386. what verb was sent in.  Return OLEOBJ_S_INVALIDVERB if you don't recognise the verb given.
  387.  
  388.     k. OnSetExtent.  This is called every time your control is resized.  The m_Size
  389. SIZEL structure is your control's current size in pixels.  controls should look in here
  390. for their size information, and override OnSetExtent if they want control over how their
  391. control is sized.  Please see the Invisible sample for an example of a control that is of
  392. a fixed size.
  393.  
  394.  
  395.     In addition, the following methods/routines can be called by an OLE control, and
  396. often prove to be extremely useful.
  397.  
  398.     a. DoSuperClassPaint.  Subclassed windows controls can call this from their 
  399. OnDraw routines to paint themselves.  For most windows controls, this will paint them 
  400. correctly in design mode and run mode.  Some Windows controls, however, will prove some-
  401. what moody, and might require a little extra tweaking.
  402.  
  403.     b. RecreateControlWindow.  Again, used for Subclassed Controls -- will go and re-
  404. create the control's HWND.  This is useful if you're changing a style bit that simply can't
  405. be changed with a SetWindowLong(GWL_STYLE ...) call.
  406.  
  407.     c. DesignMode.  Returns a BOOL indicating it's best guess as to whether you're
  408. in design mode or not.  if it can't figure it out, returns FALSE.
  409.  
  410.     d. GetAmbientProperty.  This routine is used to get an ambient property from the
  411. container.  Not all containers will return these [they might not support them], so be
  412. careful to check the return code.
  413.  
  414.     e. GetAmbientFont.  Gets the current ambient font.  Don't forget to release the
  415. font once you're done with it.  Again, the host can simply not implement this.
  416.  
  417.     f. ModalDialog.  Controls must call this before they show a modal dialog.  This
  418. is seen when you're about to show your About Box dialog.
  419.  
  420.     g. InvalidateControl.  Much like the InvalidateRect API, but this also operates in
  421. design mode.  This will force your control to be repainted if you pass in NULL for the
  422. rectangle, or will just invalidate the given area if it's not NULL.
  423.  
  424.     h. SetControlSize.  Control's who are changing their size out of OnSetExtent
  425. should use this routine to set their size.  You pass in a SIZEL structure in PIXELS, and
  426. should expect a call to OnSetExtent.  Be careful of some recursive situations.
  427.  
  428.     i. PropertyChanged.  Whenever the value of a property changes, this routine should
  429. be called to notify a host.  This will cause hosts to update any property browsers [such
  430. as those seen in Microsoft Visual Basic 4.0]
  431.  
  432.     j. RequestPropertyEdit.  Whenever you want to change a property that you've
  433. marked as requestedit in the .ODL file, you need to call this first, and check the
  434. return code.
  435.  
  436.     k. GetResourceHandle.  Controls should call this whenever they're loading a 
  437. resource that could be localized.  This will go and get the handle to the appropriate
  438. DLL, and deals with satellite DLLs or the lack thereof.  Please see the Localize sample
  439. for how this works.
  440.  
  441.     l. FireEvent.  You pass this routine an EVENTINFO structure, and an event as
  442. described in the EVENTINFO will be fired.  You also pass paramters to this routine, as it
  443. is a varargs method.
  444.  
  445.     m. ControlFromUnknown.  Property page code often finds it useful to get the
  446. COleControl * pointer from the IUnknown for a control object.  This routine does just
  447. that.
  448.  
  449.     n. Exception.  Your control, or any automation objects, can use this to 
  450. send the user an error message.  Please see Section 3.8 "Throwing an Exception"
  451. for more information on using this routine.
  452.  
  453.  
  454.  
  455.  
  456.    3.2  Painting a Control
  457.  
  458.     The OnDraw routine is called whenever you need to paint your OLE Control.  Some-
  459. times the origin is from IViewObject2::Draw [as in design mode], and other times it comes
  460. from being sent a WM_PAINT message [as handled in ControlWindowProc].
  461.  
  462.     Your control is given a DC, a rectangle to describe where to paint, a rectangle
  463. for describing a meta-file, and an Information Context [IC, passed in as an HDC] that
  464. describes the device.  If the device is a metafile, then you must do a little different
  465. work.  However, if the device is a raster display, you are typically painting to the 
  466. screen.
  467.  
  468.     Your control must be careful not to make any assumptions about the DC, except that
  469. it will be in MM_TEXT mapping mode.  Often, there will be no default pens, brushes, fonts
  470. or colors selected into the DC.  Your control will have to do this work itself.  This will
  471. typically manifest itself by having your control look slightly different in design and
  472. run modes.
  473.  
  474.  
  475.  
  476.  
  477.    3.3  Handling Messages in a Control
  478.  
  479.     Your control has a method called WindowProc, which is called whenever a message
  480. is sent to your control.  Your control should respond to messages in the desired fashion
  481. here.  Again, try to reduce the amount of work that is done in WM_CREATE, and see if it
  482. can't be put in BeforeCreateWindow.  
  483.  
  484.     For certain types of messages, such as keyboard messages for arrow keys, and other
  485. special keys, your WindowProc routine will not get called.  Instead, you'll find 
  486. OnSpecialKey called instead.  This code should look for WM_KEYDOWN/UP, WM_CHAR, and other
  487. messages and deal with them as appropriate.
  488.  
  489.     There is a certain class of messages that typically involve notify a window
  490. about happenenings that are usually sent to a window's parent.  These include WM_COMMAND
  491. WM_NOTIFY, WM_CTLCOLOR, etc.  These messages will be reflected by the host to your control
  492. in the form of OCM_COMMAND, OCM_NOTIFY, OCM_CTLCOLOR, etc.  Your controls should look for
  493. these messages instead of WM_COMMAND, etc.  Please see olectl.h for other OCM_ messages
  494. that you might be interested in.
  495.  
  496.  
  497.  
  498.  
  499.    3.4  Adding a Property
  500.  
  501.     One of the more important parts of a control is often the set of properties.  When
  502. you create a control with the wizard, you have no properties by default.  To add them is
  503. a relatively straightforward and simple process.
  504.  
  505.     First, you need to modify the primary dispatch interface for your control in the
  506. .ODL file.  For example, lets's say i've got a control called SuperScroll, and i'd like to
  507. add a LargeChange method to the control.  I'd add the following to the ISuperScroll inter-
  508. face description in the .ODL:
  509.  
  510.     [id(DISPID_LARGECHANGE), propget, helpstring("The largechange property")]
  511.         HRESULT LargeChange([out, retval] long *plLargeChange);
  512.     [id(DISPID_LARGECHANGE), propput]
  513.         HRESULT LargeChange([in] long lLargeChange);
  514.  
  515.     DISPID_LARGECHANGE is something that i define in dispids.h.  I then regenerate 
  516. the type library [.TLB] file by typing:
  517.  
  518.         make SuperScroll.TLB
  519.  
  520.     More importantly, this regenerates SuperScrollInterfaces.H.  I can then cut and
  521. paste the following two lines from said header:
  522.  
  523.  
  524.     STDMETHOD(get_LargeChange)(THIS_ long FAR* plLargeChange) PURE;
  525.     STDMETHOD(put_LargeChange)(THIS_ long lLargeChange) PURE;
  526.  
  527.     I take these two lines and add them to my class description for CSuperScroll,
  528. and make sure that I remove the PURE declarators at the end; ie:
  529.  
  530.     STDMETHOD(get_LargeChange)(long FAR* plLargeChange);
  531.     STDMETHOD(put_LargeChange)(long lLargeChange);
  532.  
  533.  
  534.     I can now implement these methods in my control file to implement my property.
  535.  
  536.     Please note that there are a few standard DISPIDs defined for you in olectl.h.  
  537. Whenever you want to declare a property, take a look in this header first to see if there
  538. is a standard dispid for it first.
  539.  
  540.  
  541.  
  542.  
  543.    3.5  Adding a Method
  544.  
  545.     Adding a method is much like adding a property to your control.  First thing
  546. one does is define a DISPID for the method.  Once you've got that, it's as simple as 
  547. adding the method to the primary interface for you control.  Controls generated with the
  548. Control wizard will already have an About method defined for them.  Lets say we'd like
  549. to define a method called MooCow, with three parameters, the last of which is optional.
  550. Here is one such method:
  551.  
  552.     [id(DISPID_MOOCOW), helpstring("Makes your Cow moo")]
  553.         HRESULT MooCow([in] long lSeconds, [in] boolean fLowPitch,
  554.                 [in, optional] VARIANT vPitch);
  555.  
  556.  
  557.     Again, as with properties, you regenerate the type library, and then paste
  558. the declaration in to your control header [without the PURE declarator] and implement
  559. it.
  560.  
  561.  
  562.  
  563.  
  564.    3.6  Adding an Event
  565.  
  566.     In a great many situations, one will want to fire an event.  For example, when
  567. one gets a WM_?BUTTONDOWN message, it often makes sense to fire a MouseDown event.  This
  568. turns out to be a surprisingly easy thing to do.
  569.  
  570.     The first thing that has to be done is the event has to be defined in an EVENTINFO
  571. structure.  There are many ways to do this, including declaring a new global variable for
  572. each event type, or using an array.  We will talk about the latter, since it's a little
  573. neater.  Let's say i want to have KeyDown, KeyUp, and KeyPushed events.
  574.  
  575.     Here's how i might declare them:
  576.  
  577.     typedef enum {
  578.         MyCtlEvent_KeyDown = 0,
  579.         MyCtlEvent_KeyUp = 1,
  580.         MyCtlEvent_KeyPushed = 2
  581.     } MYCTLEVENTS;
  582.  
  583.     VARTYPE rgI2 [] = { VT_I2 };
  584.  
  585.     EVENTINFO m_rgMyCtlEvents [] = {
  586.         { DISPID_KEYDOWN, 1, rgI2 },
  587.         { DISPID_KEYUP, 1, rgI2 },
  588.         { DISPID_KEYPUSHED, 1, rgI2 }
  589.     };
  590.  
  591.     The EVENTINFO structure has three members;  the dispid of the event, the count
  592. of arguments in the event, and a pointer to an array of VARTYPEs that describe the types
  593. of the parameters to the event.  Please note, again, that there are a bunch of DISPIDs
  594. defined for you in olectl.h.  Whenever you're adding an event, go check there first to see
  595. if there's already a DISPID for it.
  596.  
  597.  
  598.     To fire these events from code, i can just call the following:
  599.  
  600.     FireEvent(&(m_rgMyCtlEvents[MyCtlEvent_KeyDown]), sKeyValue);
  601.  
  602.  
  603.  
  604.  
  605.    3.7  Using Standard OLE Types
  606.  
  607.     Many controls will find it useful to declare properties of types provided by OLE,
  608. such as Font, Picture, and Color.  Many hosts will detect properties of these types and
  609. put up convenient browsers for the user to select values for these types.
  610.  
  611.     To declare a property of one of these types, one must first make sure their .ODL 
  612. includes the following at the top:
  613.  
  614.     importlib(STDTYPE_TLB);
  615.  
  616.     Then, to declare a property of type Font, Picture, or Color, one would do some-
  617. thing similar to the following, depending on the type:
  618.  
  619.     [id(DISPID_FONT), propget]
  620.         HRESULT Font([out, retval] IFontDisp **ppFont);
  621.     [id(DISPID_FONT), propput]
  622.         HRESULT Font([in] IFontDisp *pFont);
  623.  
  624.     [id(DISPID_MOUSEICON), propget]
  625.         HRESULT MouseIcon([out, retval] IPictureDisp **ppMouseIcon);
  626.     [id(DISPID_MOUSEICON), propput]
  627.         HRESULT MouseIcon([in] IPictureDisp *pMouseIcon);
  628.  
  629.     [id(DISPID_FORECOLOR), propget]
  630.         HRESULT ForeColor([out, retval] OLE_COLOR *pocForeColor);
  631.     [id(DISPID_FORECOLOR), propput]
  632.         HRESULT ForeColor([in] OLE_COLOR ocForeColor);
  633.  
  634.  
  635.     For the get_ and put_ methods for these types, you'll get a property as declared
  636. above.  For font's and pictures, you'll probably want to QI these for IFont and IPicture
  637. respectively.  See the FontColor control sample for an idea of how this is done.
  638.  
  639.     MSDN contains some very good descriptions of how to use these fonts in your
  640. application, but the following is a short run-down.
  641.  
  642.     To use a font object in your control, you'll typically call the get_hFont
  643. method, and pass the resulting HFONT to your DC.  Pictures will be much the same.
  644.  
  645.     To use a color, you'll want to call OleTranslateColor to convert it to a real
  646. COLORREF.  OLE_COLORs are basically COLORREFs with some support for 'generic' colors,
  647. such as COLOR_WINDOW, COLOR_WINDOWTEXT, etc.  To convert one of these into an OLE_COLOR,
  648. just OR [|] them with 0x80000000.  Ie, to initialize your background to COLOR_WINDOW, set
  649. your backcolor property to COLOR_WINDOW | 0x80000000.  Then, to paint your backdrop, just
  650. call OleTranslateColor(), and use the resulting colorref.
  651.  
  652.     NOTE:  The Framework uses dual/vtable bound automation interfaces, and uses
  653. OLE Automation functionality to support IDispatch methods on the automation objects.
  654. There is a known problem in OLE automation, which will cause problems [unexpected failures
  655. and/or crashes] when using the provided ITypeInfo::Invoke on properties that are declared
  656. to be of types that are imported from a type library [ie, any font, picture, or color
  657. property has this problem.]
  658.  
  659.     The way to get around this problem is to override Invoke(), and to look for the 
  660. DISPIDs of your properties that are of this type.  In this case, you can quickly dispatch
  661. the call to the appropriate member function yourself.  The FontColor sample does just
  662. this.  See it's implementation of IDispatch::Invoke for sample code on how to work around
  663. it.  This problem will not exist in a future version of OLE Automation, but until then,
  664. the workaround is necessary -- but fortunately not terribly expensive.
  665.  
  666.  
  667.  
  668.    3.7 Throwing an Exception
  669.  
  670.     Every once in a while, during an operation, your control will find
  671. itself rather upset with the state of the union, and will wish to communicate this
  672. to the user.
  673.  
  674.     The way to do this is via an exception.  In any of your OLE Automation methods
  675. or property operators, you can call the Exception method when exiting, and it will set
  676. up all the appropriate information to trigger the error.
  677.  
  678.     For Example:
  679.  
  680.     CMyControl::put_ButtZilla(long lButtZilla)
  681.     {
  682.         if (ButtZilla == 10)
  683.             return Exception(MYCTL_E_IHATETHENUMBER10, IDS_ERR_IHATETHENUMBER10, 0);
  684.         m_lButtZilla = lButtZilla;
  685.         return S_OK;
  686.     }
  687.  
  688.     The arguments to the Exception routine are as the follows.  The first is the
  689. SCODE of the error you wish to trigger.  For errors unique to your control, you
  690. should define them something like:
  691.  
  692.     #define MYCTL_E_IHATETHENUMBER10 MAKE_SCODE(SEVERITY_ERROR, FACILITY_CONTROL, 34500)
  693.  
  694.     The second argument is the resource id of the string that you should display.
  695. The Exception code will correctly get this information from your localized Satellite
  696. DLL.
  697.  
  698.     Finally, the last argument is the helpcontextid that will passed to the helpfile
  699. you defined in your object's structure.
  700.  
  701.  
  702.  
  703. 4.0 Persistence
  704. =-------------=
  705.  
  706.     One of the most important things your control will do is save out and restore it's
  707. persistent state.  This will typically be done in one of two ways; through PropertyBags
  708. for text persistence, and through Streams for binary persistence.  In the latter, 
  709. performance is absolutely critical to make your control load quickly.
  710.  
  711.     The OLE Controls Framework requires that your control implement four member func-
  712. tions to support persistence.  You are required to implement LoadTextState,
  713. LoadBinaryState, SaveTextState, and SaveBinaryState.  If you are positive that you are
  714. never going to be in a host that uses IPersistPropertyBag, then you can ignore the two 
  715. text interfaces, but this isn't recommended [they prove sufficiently straightforward to
  716. use].
  717.  
  718.  
  719.  
  720.  
  721.    4.1  Text Persistence
  722.  
  723.     Text persistence in the Framework is done via IPersistPropertyBag and IPropertyBag.
  724. All OLE controls have an implementation of IPersistProperty Bag, and are given pointers to
  725. PropertyBag objects to do their work.
  726.  
  727.     MSDN and Craig Brockschmidt's "Inside OLE 2 [2nd ed]" both have descriptions of
  728. the IPropertyBag interface.  Effectively, there are two routines that the programmer will
  729. use:  Read and Write.  In both cases, the programmer will pass in a VARIANT.  in the
  730. Read case, the property, if it was saved out, will be put in the VARIANT.  If the property
  731. couldn't be found [it wasn't ever saved out], then the default value for that property
  732. should be used, and an error should probably not be returned.  For the Write, a VARIANT
  733. with the data is passed in.  To persist out a collection or an object [such as a Font or
  734. Picture object], one can pass in a VT_UNKNOWN object, and the PropertyBag will then
  735. QI that object for IPersistPropertyBag or IPersistStream and persist it.  This actually
  736. proves effective for persisting collections -- they can just support IPersistPropertyBag.
  737.  
  738.     All of the samples except the Localize and Circle sample have examples of how to
  739. persist out properties using PropertyBags.  For controls with many properties, it often
  740. makes sense to head to some sort of table driven persistence to reduce code size and bug
  741. potential.
  742.  
  743.  
  744.  
  745.  
  746.    4.2  Binary Persistence
  747.  
  748.     Binary persistence turns out to be the more critical things to work on when
  749. implementing an OLE control.  Control Load speed can be severely hampered by a poorly
  750. written LoadBinaryState routine, so it's rather critical to spend some time thinking about
  751. how to keep this routine fast. The binary persistence code is used by many hosts all the 
  752. time, and by other hosts when including the control in a generated executable file.
  753.  
  754.     In both routines, you are handed a pointer to an IStream object.  The key to load
  755. speed here is to reduce the number of operations on the stream.  For save, this is 
  756. slightly less critical.
  757.  
  758.     Typically, a control will want to save out the following information [often in the
  759. given order]:
  760.  
  761.     - some sort of header with a magic number, version, and size information
  762.     - fixed size state information, such as longs, floats, colors, strings, etc.
  763.     - variable sized persisten state, such as fonts, pictures, collections, etc.
  764.  
  765.     Most control writers will want to start out their binary persistent state with
  766. some sort of header structure that includes a 'magic' number that you can check for when
  767. you're loading for sanity.  You'll also want to include some sort of version number so that
  768. future versions of your control can deal with older versions, and finally one will often
  769. want to write out the number of bytes of data that were written.
  770.  
  771.     The samples in the framework that have a binary persistent state use the following
  772. structure:
  773.  
  774.     #define STREAMHDR_MAGIC    0x12345678
  775.  
  776.     typedef struct {
  777.             DWORD dwMagic;
  778.             DWORD dwVersion;
  779.             DWORD cbSize;
  780.     } STREAMHDR;
  781.  
  782.     The SaveBinaryState routine saves out this information, and the LoadBinaryState
  783. routine looks for it.
  784.  
  785.     One way to write out all the fixed size information [and therefore load it
  786. efficiently] is to do it all in one chunk -- if all your fixed persistent state is in
  787. a structure in your control object, then you can just write out said structure in the
  788. persistence code.  Controls generated by the control wizard will have a structure
  789. defined for them called MYCTLNAMECTLSTATE which users can opt to put their fixed state
  790. data into.  Then, when saving, they can just do the following:
  791.  
  792.     hr = pStream->Write(&m_state, sizeof(m_state), NULL);
  793.  
  794.     For loading, it becomes as simple as:
  795.  
  796.     hr = pStream->Read(&(m_state), sizeof(m_state), NULL);
  797.  
  798.     Extremely efficient and simple.
  799.  
  800.     For fonts and pictures, it's slightly more complicated.  Effectively, one has
  801. to QI those objects for IPersistStream, and then call the Load or Save routine with the
  802. stream that you've been given.  Typically, this can be done after you've written out all
  803. other information.  The FontColor control sample does just this.
  804.  
  805.     If you follow the above suggestions for persistent state structure, you can
  806. typically have your load routine look something like as follows:
  807.  
  808.  
  809.     IPersistStream *pps;
  810.     STREAMHDR sh;
  811.     HRESULT   hr;
  812.  
  813.     // first read in the streamhdr, and make sure we like what we're getting
  814.     //
  815.     hr = pStream->Read(&sh, sizeof(sh), NULL);
  816.     RETURN_ON_FAILURE(hr);
  817.  
  818.     // sanity check
  819.     //
  820.     if (sh.dwMagic != STREAMHDR_MAGIC || sh.cbSize != sizeof(m_state))
  821.         return E_UNEXPECTED;
  822.  
  823.     // read in the control state information
  824.     //
  825.     hr = pStream->Read(&(m_state), sizeof(m_state), NULL);
  826.     RETURN_ON_FAILURE(hr);
  827.  
  828.  
  829.     // now read in the font!
  830.     //
  831.     OleCreateFontIndirect(&_fdDefault, IID_IFont, (void **)&m_pFont);
  832.     RETURN_ON_NULLALLOC(m_pFont);
  833.  
  834.     // qi it for ipersiststream and load it in.
  835.     //
  836.     hr = m_pFont->QueryInterface(IID_IPersistStream, (void **)&pps);
  837.     RETURN_ON_FAILURE(hr);
  838.  
  839.     hr = pps->Load(pStream);
  840.     pps->Release();
  841.  
  842.     return hr;
  843.  
  844.  
  845.     This proves to be acceptably fast and robust.
  846.  
  847.  
  848. 5.0 Property Pages
  849. =----------------=
  850.  
  851.     Most OLE Controls will find property pages an invaluble addition to their design
  852. time functionality.  Fortunately, implementing them proves to be relatively straight-
  853. forward.  To do so, one merely needs to declare an object that inherits from 
  854. CPropertyPage.
  855.  
  856.  
  857.  
  858.  
  859.    5.1 Working with a Property Page
  860.  
  861.     Your property page is declared in the header file using the DEFINE_PROPERTYPAGE
  862. macro, which puts it into the g_ObjectInfo table.  The framework supports the creation
  863. of the property page object, but you are required to implement the static Create() 
  864. function [which the control wizard generates for you].
  865.  
  866.     The property page is created much like a regular windows dialog box would be -- 
  867. you use your favorite resource editor to create a DIALOG resource, and then just cut and
  868. paste it into your control's resource [.RC] file.
  869.  
  870.     The most important method you'll have to implement will be the DialogProc method,
  871. which is where all the work will take place.  In addition to the regular windows messages
  872. that one would expect in a DialogProc, there are three additional ones which people 
  873. working with this framework will expect:
  874.  
  875.     a. PPM_NEWOBJECTS  -- your control has been given some new objects.  You are 
  876. expected to go and populate your page's controls with information from this object.  Using
  877. the FirstControl() and NextControl() methods from the CPropertyPage class, you can get the
  878. relevant information.
  879.  
  880.     b. PPM_APPLY -- you have to apply any changes that have occurred now.  Again, you
  881. can use the FirstControl() and NextControl() routines to loop through all the objects for
  882. which the property pages were visible and apply the values [note that it's possible for
  883. there to be more than one object for which a property page is being displayed].
  884.  
  885.     c. PPM_EDITPROPERTY -- when you are sent this message, you are expected to set the
  886. focus to the control which represents the property of the given DISPID.  You will typically
  887. only see this message called if you implement IPerPropertyBrowsing and return a value
  888. in MapPropertyToPage.
  889.  
  890.     Please see one of the sample controls for exact details on these messages.
  891.  
  892.  
  893.  
  894.  
  895.    5.2 Navigating through Associated Objects
  896.  
  897.     Your property pages will operate on one or more controls.  When initializing, one
  898. will typically get some values from the first control that you are given.  you can use
  899. the FirstControl() method to get the object pointer for this control.  You can then QI
  900. it for you primary dispatch interface to get properties to populate the page with.
  901.  
  902.     When told to apply the values [PPM_APPLY], you'll want to apply them to all
  903. objects, which means you'll want to loop using FirstControl and NextControl(), as follows:
  904.  
  905.         for (pUnk = FirstControl(&dwCookie) ; pUnk; pUnk = NextControl(&dwCookie)) {
  906.             hr = pUnk->QueryInterface(IID_IButton, (void **)&pButton);
  907.             if (FAILED(hr)) continue;
  908.     
  909.             GetDlgItemText(hwnd, IDC_CAPTION, szTmp, 128);
  910.             bstr = BSTRFROMANSI(szTmp);
  911.             ASSERT(bstr, "Maggots!");
  912.             pButton->put_Caption(bstr);
  913.             SysFreeString(bstr);
  914.             pButton->Release();
  915.         }
  916.  
  917.     Please note that the return values of FirstControl and NextControl don't need to
  918. be Release()'d.
  919.  
  920.  
  921.  
  922.  
  923.    5.3 Marking your Page as Dirty.
  924.  
  925.     It is moderately important to correctly mark your property page as dirty at the
  926. appropriate times.  This must be done manually.  Typically, one will do this in response
  927. to a windows notification message, such as EN_CHANGE or BN_CLICKED.  When you wish to
  928. mark your page as dirty, the MakeDirty() routine should be called.  This will cause the
  929. Apply button to be enabled, if it was previously disabled, and will tell the host that
  930. you should be saved before destroying the page.
  931.  
  932.     The following code causes the page to mark itself as dirty when the user changes
  933. the text in a Text box in the property page:
  934.  
  935.           case WM_COMMAND:
  936.             switch (LOWORD(wParam)) {
  937.               case IDC_CAPTION:
  938.                 if (HIWORD(wParam) == EN_CHANGE)
  939.                     MakeDirty();
  940.                         break;
  941.             }
  942.             break;
  943.  
  944.  
  945.  
  946.  
  947. 6.0  String Manipulation
  948. =----------------------=
  949.  
  950.     The OLE Controls framework provides a robust system of macros for manipulating
  951. strings in pretty much all of the ways that you'll run into while working with an OLE
  952. control.
  953.  
  954.  
  955.  
  956.    6.1 Types of Strings
  957.  
  958.     Under 32bits, there are a few different types of strings, and understanding these
  959. tends to be pretty important when working with OLE, since there is great potential for
  960. memory leaks and bugs associated with strings.
  961.  
  962.     There are two fundamental types of strings --  Multi-Byte [which can be ANSI or
  963. double byte] and Unicode strings.  Of the former, one almost always works with some sort
  964. of char * pointer [LPSTR, LPCSTR].  Of the latter, there are a few types that are commonly
  965. used.  Most notably, there are WCHAR * [LPWSTR, LPWCSTR], BSTR, and OLESTR strings.
  966.  
  967.     LPWSTR pointers are just that -- a pointer to a wide string.  An LPOLESTR pointer
  968. is much the same, with some additional OLE rules added to it.  An OLESTR is merely a
  969. wide string, but when it's an out-parameter to a function, it should be allocated using
  970. the host's IMalloc allocator [ie, CoTaskMemAlloc].
  971.  
  972.     A BSTR is a string with a little more structure [specifically, a length prefix].
  973. To work with BSTRs, you need to use special APIs designed exclusively form them, notably
  974. SysAllocString, SysFreeString, and SysStringLen [there are a few others.  See the OLE
  975. Programmers Reference, Volume II for more details].
  976.  
  977.     These data types are fully interchangeable as far as compares and copies go, but
  978. they are NOT interchangeable as far as allocation and freeing go -- it is not acceptable
  979. to call SysFreeString on an OLESTR or LPWSTR string.
  980.  
  981.     Both BSTRs and OLESTRs as in-parameters to functions should not be freed [as per
  982. standard OLE COM conventions].  By the same token, BSTRs and OLESTRs as out params should
  983. be expected to be freed, and should thus be allocated appropriately.
  984.  
  985.  
  986.  
  987.  
  988.    6.2 Working with Strings
  989.  
  990.     Now, the problem is that, for the most part, your controls will be be working with
  991. Multi-byte strings, excepting for where you work with OLE.  Therefore, there will be
  992. various scenarios where you'll either be given a wide string, and need the multi-byte
  993. version of it, or you'll have a multi-byte string, and need a wide string for it.
  994.  
  995.     To solve these problems, the OLE Controls Framework includes the following
  996. macros to work with:
  997.  
  998.     MAKE_WIDEPTR_FROMANSI(newstringname, convertme)
  999.     MAKE_ANSIPTR_FROMWIDE(newstringname, convertme)
  1000.  
  1001.     BSTRFROMANSI(ansistr)
  1002.     OLESTRFROMANSI(ansistr)
  1003.     BSTRFROMRESID(resourceid)
  1004.     OLESTRFROMRESID(resourceid)
  1005.     COPYOLESTR(copyme)
  1006.     COPYBSTR(copyme)
  1007.  
  1008.     The first two macros will take a string of a given type and a name, and create
  1009. a variable of the new name [do -not- declare a variable of this name yourself], and then
  1010. convert the other string into the new variable.  This cannot be used as an rvalue in C/C++
  1011. expressions, nor can it be an lvalue [it pretty much needs to sit on a line by itself].
  1012.  
  1013.     The last set of macros pretty much do all the remaining interesting work.  You can
  1014. get BSTRs or IMalloc'd OLESTRs from an ANSI string, or copy OLESTRs and BSTRs.  The only
  1015. additional functions of real interest are those that take a WORD, which is a resource id,
  1016. and loads in a string from your localization DLL [or the main if you don't do satellite
  1017. localization] and makes either a BSTR or OLESTR out of it.  This proves useful in a few
  1018. places where you need a localized string.
  1019.  
  1020.     Remember that while these macros were designed with a certain amount of speed in
  1021. mind, converting strings is still not a ridiculously cheap operation, and control writers
  1022. should try to be moderately conservative in string conversions.
  1023.  
  1024.  
  1025. 7.0  Localization
  1026. =---------------=
  1027.  
  1028.     The OLE Controls framework also has some support for robust localization of your
  1029. control, most notably, property pages, type libraries, and anything else you find
  1030. interesting to localize.
  1031.  
  1032.     The scheme is follows:  the resources for the default language [typically english]
  1033. are in the main in-proc server's resources.  Then, for each additional language supported,
  1034. there is a satellite DLL which contains the type library and resources for that language.
  1035. In the main in-proc server you define a table in the resources which has all of the
  1036. supported languages and their localized DLLs names.  
  1037.  
  1038.     The table will look as follows:
  1039.  
  1040.  
  1041. INTLSZ_LANGMAP RCDATA DISCARDABLE 
  1042. BEGIN
  1043. //    Primary Language ID  SubLanguage ID          Satalite DLL
  1044. //    -------------------  --------------          ------------
  1045.     LANG_FRENCH,         SUBLANG_SWISS,          "LocalizeFS.DLL", "\0",
  1046.     LANG_FRENCH,         SUBLANG_FRENCH,         "LocalizeFR.DLL","\0",
  1047.     LANG_JAPANESE,       SUBLANG_DEFAULT,        "LocalizeJP.DLL","\0",
  1048. END
  1049.  
  1050.  
  1051.     The search rules are as follows:  If you find an exact match of both the
  1052. primary langid and sublandid for the current ambient LCID in the table, then return the
  1053. DLL name for that entry.  If only a primary langid match is found, then return the last
  1054. matching entry with that primary langid from the table.  [thus FRENCH/FRENCH comes after
  1055. FRENCH/SWISS].  If there are no matches, the currently running DLL is used.
  1056.  
  1057.     Thus, if running under a FRENCH/SWISS system, you would use LocalizeFS.DLL.  If
  1058. running under a FRENCH/CANADIAN system, you'd ust LocalizeFR.DLL, and if you were running
  1059. under a SPANISH/MEXICAN system, you'd use Localize.Ocx for resources.
  1060.  
  1061.  
  1062.  
  1063.    7.1  Setting up for Localization
  1064.  
  1065.     If you want to support satellite localization in your in-proc server, then you
  1066. need to set up a couple of things.
  1067.  
  1068.     First, you'll need to set the variable g_fSatelliteLocalization in your in-proc
  1069. server file to TRUE.  This will instruct all further code paths to use satellite local-
  1070. ization.   You'll also need to set up the above table in your .RC file for the main
  1071. in-process server.  Add entries for the langauges you wish to support.
  1072.  
  1073.     Whenever you want to load a resource, make sure you use GetResourceHandle() to
  1074. get the instance handle for the localized resources.  This will traverse the table and
  1075. find the correct satellite DLL and load it.
  1076.  
  1077.     Please see the Localize sample included with the framework for an example of how
  1078. this works, along with an actual localized satellite DLL [in the \French subdirectory].
  1079. The satellite DLL should contain all localized resources you're interested in as well as
  1080. a localized type library.  Don't forget to mark the localized type library with the LCID
  1081. of the intended language.
  1082.  
  1083.  
  1084.  
  1085. 8.0 Internet Controls
  1086. =-------------------=
  1087.  
  1088. Making your control Internet aware is simple.  What follows is a description of
  1089. the steps required.
  1090.  
  1091. 1. Make sure your control inherits from CInternetControl rather than 
  1092.    COleControl.
  1093.     
  1094. 2. Call the SetupDownload method with either a URL, Moniker, or a 
  1095.    PropertyStream to initiate the download of the data.
  1096.  
  1097. 3. Implement the OnData method to receive the stream information.  The control 
  1098.    should understand how to process the data as it arrives to improve the user 
  1099.    experience.  In URLDib2, the control distinguishes between bitmap header 
  1100.    data and actual bits, and begins to create the DC etc. while it is waiting 
  1101.    for more bits to come down.
  1102.  
  1103. 4. <Optional> Implement the OnProgress method to be called while data is being
  1104.    downloaded.  A control can determine what percentage of the data has been
  1105.    transferred, to determine how long to wait, display a progress gauge etc.
  1106.  
  1107.  
  1108. Additional Support
  1109. The following methods are also available:
  1110.  
  1111. GetAMoniker - turn a URL into a Moniker
  1112.  
  1113. GetAsyncHost - In case you want to know the host
  1114.  
  1115. GetBinding - converts a propId to an IBinding
  1116.  
  1117. InternalQueryInterface - allows you to expose your own interfaces.
  1118.  
  1119.  
  1120.  
  1121.  
  1122. 9.0 Miscellaneous
  1123. =---------------=
  1124.  
  1125.  
  1126.    9.1  Recommended Reading
  1127.  
  1128.     Inside OLE2, Second Edition, Kraig Brockschmidt, Microsoft Press.
  1129.     - Chapters 3, 13-15 and 24 contain most of the information you'll need to work
  1130.       with an OLE control and/or automation server.  The chapters on persistence should
  1131.       also prove useful for those unfamiliar with them.
  1132.       
  1133.  
  1134.     OLE2 Programmers Reference, Volume I and II, Microsoft press.
  1135.  
  1136.  
  1137.     MFC Source Code, Visual C++ 4.0.
  1138.     - when looking for ideas on how to do something, the CDK source code proves to be
  1139.       an invaluble resource.  Use it -- frequently.
  1140.  
  1141.  
  1142.    9.2 Host Specific Notes
  1143.  
  1144.     Then following are notes, things to consider, or known problems with specific
  1145.     OLE Controls hosts
  1146.  
  1147.       9.2.1 Microsoft Access 95
  1148.  
  1149.     Access 95 -requires- IPerPropertyBrowsing.  If you do not implement this routine,
  1150.     you will not able to put one of your controls on a form.  See MSDN and the MFC
  1151.     source code for documentation on this method.
  1152.  
  1153.     In addition, you cannot return error scodes from GetPredefinedStrings. You must 
  1154.     return either S_OK or S_FALSE.  Returning an error scode will crash.  
  1155.     MapPropertyToPage should still return PERPROP_E_NOPAGEAVAILABLE if there are no
  1156.     pages available.
  1157.